package com.platform.core.service.impl;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.platform.core.entity.*;
import com.platform.core.repository.NcloudSysUserPostRepository;
import com.platform.core.repository.SysUserRepository;
import com.platform.dto.*;
import com.platform.enums.*;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.JPAExpressions;
import com.platform.comm.dto.DtoMapper;
import com.platform.comm.enums.YnEnum;
import com.platform.comm.exceptions.RestApiException;
import com.platform.comm.jwt.JwtTokenUtil;
import com.platform.core.security.auth.AuthenticationFacade;
import com.platform.core.service.SysDepartService;
import com.platform.core.service.SysUserRoleService;
import com.platform.core.service.SysUserService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static com.platform.comm.constants.GlobalConstant.BASE_REDIS_CACHE_PREFIX;
import static com.platform.comm.constants.GlobalConstant.DATE_TIME_PATTERN;

/**
 * Description: 用户-Service实现类
 *
 * @author libin Created on 2019/7/15
 **/
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class SysUserServiceImpl extends BaseEntityBizImpl<SysUserRepository, SysUser> implements SysUserService {

    private final QSysDepart qSysDepart = QSysDepart.sysDepart;
    private final QSysUser qSysUser = QSysUser.sysUser;
    private final QSysUserRole qSysUserRole = QSysUserRole.sysUserRole;
    private final QSysRole qSysRole = QSysRole.sysRole;
    private final PasswordEncoder passwordEncoder;
    private final DtoMapper dtoMapper;
    private final SysUserRoleService sysUserRoleService;
    private final SysDepartService sysDepartService;
    private final JwtTokenUtil jwtTokenUtil;
    private final RedisTemplate<String, Object> redisTemplate;
    private final NcloudSysUserPostRepository userPostRepository;
    private final QNcloudSysUserPostEntity userPostEntity = QNcloudSysUserPostEntity.ncloudSysUserPostEntity;
    // 用户信息缓存key
    static final String USER_INFO_KEY = BASE_REDIS_CACHE_PREFIX + "userInfo";

    public SysUserServiceImpl(SysUserRepository baseRepository, @Lazy PasswordEncoder passwordEncoder,
                              DtoMapper dtoMapper, SysUserRoleService sysUserRoleService, SysDepartService sysDepartService,
                              JwtTokenUtil jwtTokenUtil, RedisTemplate<String, Object> redisTemplate,
                              NcloudSysUserPostRepository userPostRepository) {
        super(baseRepository);
        this.passwordEncoder = passwordEncoder;
        this.dtoMapper = dtoMapper;
        this.sysUserRoleService = sysUserRoleService;
        this.sysDepartService = sysDepartService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.redisTemplate = redisTemplate;
        this.userPostRepository = userPostRepository;
    }

    @Override
    public Page<UserQo> queryUsers(Predicate predicate, Pageable pageable, MultiValueMap<String, String> parameters, String accessSubOrg) {
        if (pageable.getSort().isUnsorted()) {
            pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(),
                    QSort.by(qSysUser.sortNum.asc(), qSysUser.createDate.desc()));
        }
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N));
        if (AccessSubOrg.Y.getCode().equals(accessSubOrg)) {
            builder = builderConditions(predicate, parameters, SysUser.class, qSysUser.getMetadata());
        }
        String departId = parameters.getFirst("departId");
        if (!Strings.isNullOrEmpty(departId)) {
            builder.and(qSysUser.departId.eq(departId));
        }

        String id = parameters.getFirst("id");
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysUser.id.eq(id));
        }

        final String commType = parameters.getFirst("status");
        if (Strings.isNullOrEmpty(commType) || Objects.equals(commType, CommFlagType.Enabled.getFvalue())) {
            builder.and(qSysUser.status.eq(CommFlagType.Enabled).or(qSysUser.status.eq(CommFlagType.Initial)));
        }
        final String username = parameters.getFirst("username");
        if (!Strings.isNullOrEmpty(username)) {
            builder.and(qSysUser.username.contains(username));
        }
        final String realName = parameters.getFirst("realName");
        if (!Strings.isNullOrEmpty(realName)) {
            builder.and(qSysUser.realName.contains(realName));
        }
        Page<SysUser> page = queryByPage(builder, pageable);

        return page.map(entity -> {
            UserQo userQo = dtoMapper.map(entity, UserQo.class);
            DepartQo depart = sysDepartService.findById(userQo.getDepartId());
            List<RoleQo> list = sysUserRoleService.findRolesByUserIdWithoutParent(userQo.getId());
            list.forEach(roleQo -> {
                if (roleQo.getRoleType().equals(RoleType.I)) {
                    RoleQo sysRole = sysUserRoleService.findByUserIdAndRoleId(userQo.getId(), roleQo.getId());
                    roleQo.setAccessSubOrg(Objects.nonNull(sysRole) ? sysRole.getAccessSubOrg() : null);
                }
            });

            List<DepartQo> posts = Lists.newArrayList();
            userPostRepository.findAll(userPostEntity.userId.eq(userQo.getId()))
                    .forEach(post ->
                            posts.add(sysDepartService.queryDepartById(post.getDepartId())));

            userQo.setDepartKind(depart.getDepartKind());
            userQo.setDepartName(depart.getDepartName());
            userQo.setRoles(list);
            userQo.setPosts(posts);
            return userQo;
        });
    }

    //    @Cacheable(value = BASE_REDIS_CACHE_PREFIX + "user",
//            key = "T(com.platform.core.security.auth.AuthenticationFacade).userCacheKey",
//            condition = "T(com.platform.core.security.auth.AuthenticationFacade).userCacheKey != null ")
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    @Override
    public UserDetail findByUsername(String username) {
        Optional<Tuple> optional = repository.findOne(qSysUser.username.eq(username).and(qSysUser.delFlag.eq(YnEnum.N)),
                jpqlQuery -> jpqlQuery.leftJoin(qSysDepart).on(qSysUser.departId.eq(qSysDepart.id)),
                qSysUser, qSysDepart.id, qSysDepart.departName, qSysDepart.departCode, qSysDepart.fullName, qSysDepart.departKind);
        return optional.map(tuple -> {
            SysUser sysUser = tuple.get(qSysUser);
            List<RoleQo> list = sysUserRoleService.findRolesByUserId(Objects.requireNonNull(sysUser).getId());
            UserDetail userDetail = dtoMapper.map(sysUser, UserDetail.class);
            userDetail.setDepartId(tuple.get(qSysDepart.id));
            userDetail.setDepartName(tuple.get(qSysDepart.departName));
            userDetail.setDepartCode(tuple.get(qSysDepart.departCode));
            userDetail.setDepartFullName(tuple.get(qSysDepart.fullName));
            userDetail.setDepartKind(tuple.get(qSysDepart.departKind));
            userDetail.setRoles(list);
            return userDetail;
        }).orElse(null);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserQo create(UserQo userQo) {
        // 校验用户名唯一
        long count = checkUsernameExists("", userQo.getUsername());
        if (count > 0) {
            throw new RestApiException("用户名称不能重复！");
        }
        userQo.init();
        SysUser entity = dtoMapper.map(userQo, SysUser.class);
        if (!Strings.isNullOrEmpty(entity.getPassword())) {
            entity.setPassword(passwordEncoder.encode(entity.getPassword()));
        }
        saveEntity(entity);
        // 分配默认角色
        addRoles(userQo, entity);

        // 分配岗位
        addPosts(userQo, entity);
        return dtoMapper.map(entity, UserQo.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserQo createOtherUser(UserQo userQo) {
        return this.create(userQo);
    }

    private void addPosts(UserQo userQo, SysUser entity) {
        if (Objects.isNull(userQo.getPosts())) {
            return;
        }
        List<NcloudSysUserPostEntity> posts = userQo.getPosts().stream().map(userPostEntity -> {
            NcloudSysUserPostEntity userPost = new NcloudSysUserPostEntity();
            userPost.setUserId(entity.getId());
            userPost.setDepartId(userPostEntity.getId());
            userPost.setCreateDate(LocalDateTime.now());
            userPost.setCreateBy(AuthenticationFacade.getUserId());
            return userPost;
        }).collect(Collectors.toList());
        userPostRepository.saveAll(posts);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserQo shareCreate(ExternalUserQo userQo) {
        // 验证登录名唯一
        long usernameCount = checkUsernameExists(null, userQo.getUsername());
        Assert.isTrue(usernameCount == 0, "登录名称不能重复");
        // 验证邮箱唯一
        if (!Strings.isNullOrEmpty(userQo.getEmail())) {
            long emailCount = checkEmailExists(null, userQo.getEmail());
            Assert.isTrue(emailCount == 0, "邮箱不能重复");
        }
        Assert.notNull(userQo.getInfoSource(), "信息来源不能为空");
        Assert.notNull(userQo.getDeptUnifiedCode(), "机构社会信用代码不能为空");
        DepartQo departQo = sysDepartService.findByDeptUnifiedCode(userQo.getDeptUnifiedCode());
        Assert.notNull(departQo, "机构社会信用代码没有对应的信息");
        userQo.init();
        SysUser userEntity = dtoMapper.map(userQo, SysUser.class);
        userEntity.setDepartId(departQo.getId());

        if (!Strings.isNullOrEmpty(userEntity.getPassword())) {
            userEntity.setPassword(passwordEncoder.encode(userEntity.getPassword()));
        }
        saveEntity(userEntity);
        UserQo map = dtoMapper.map(userQo, UserQo.class);
        // 分配默认角色
        addRoles(map, userEntity);
        return dtoMapper.map(userEntity, UserQo.class);
    }


    //    @Caching(evict = {
//            @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#userQo.id"),
//            @CacheEvict(value = {BASE_REDIS_CACHE_PREFIX + "roleFunctions"}, key = "#userQo.id")
//    })
    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserQo modify(UserQo userQo) {
        SysUser entity = fetchById(userQo.getId());
        dtoMapper.map(userQo, entity);
        entity.setUpdateDate(LocalDateTime.now());
        saveEntity(entity);
        clearRedis();
        // 更新角色
        // 分配默认角色
        sysUserRoleService.removeByUserId(entity.getId());
        addRoles(userQo, entity);
        // 分配岗位
        userPostRepository.deleteAll(userPostRepository.findAll(userPostEntity.userId.eq(entity.getId())));
        addPosts(userQo, entity);
        return dtoMapper.map(entity, UserQo.class);
    }

    private void addRoles(UserQo userQo, SysUser entity) {
        if (CollectionUtils.isEmpty(userQo.getRoles())) {
            sysUserRoleService.create(entity.getId(), RoleEnum.ROLE_LOGIN.name());
        } else {
            sysUserRoleService.createInRoleInfo(entity.getId(), userQo.getRoles());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void modifyImportId(UserQo userQo) {
        SysUser entity = fetchById(userQo.getId());
        dtoMapper.map(userQo, entity);
        entity.setUpdateDate(LocalDateTime.now());
        saveEntity(entity);
        //分配默认角色
        sysUserRoleService.create(userQo.getId(), RoleEnum.ROLE_LOGIN.name());
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", allEntries = true)
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void changeStatus(List<String> ids, CommFlagType commFlagType) {
        ids.stream().map(this::fetchById)
                .filter(sysUser -> !sysUser.getStatus().equals(CommFlagType.Initial))
                .forEach(sysUser -> {
                    sysUser.setStatus(commFlagType);
                    sysUser.setUpdateDate(LocalDateTime.now());
                });
        repository.flush();
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#id")
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void changeStatus(String id, CommFlagType commFlagType) {
        Assert.notNull(id, "用户编码不能为空");
        SysUser sysUser = fetchById(id);
        if (ADMINISTRATOR_CODE.equals(sysUser.getUserCode())) {
            throw new RestApiException("不允许禁用管理账号");
        }
        if (id.equals(AuthenticationFacade.getUserId())) {
            throw new RestApiException("不允许禁用自己的账号");
        }
        if (!sysUser.getStatus().equals(CommFlagType.Initial)) {
            sysUser.setStatus(commFlagType);
            sysUser.setUpdateDate(LocalDateTime.now());
            saveEntity(sysUser);
        }
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", allEntries = true)
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void changeDelFlag(List<String> ids, YnEnum ynEnum) {
        ids.stream().map(this::fetchById)
                .filter(sysUser -> !sysUser.getStatus().equals(CommFlagType.Initial))
                .forEach(sysUser -> {
                    sysUser.setDelFlag(ynEnum);
                    sysUser.setUpdateDate(LocalDateTime.now());
                });
        repository.flush();
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#id")
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void changeDelFlag(String id, YnEnum ynEnum) {
        SysUser sysUser = fetchById(id);
        if (!sysUser.getStatus().equals(CommFlagType.Initial)) {
            sysUser.setDelFlag(ynEnum);
            sysUser.setUpdateDate(LocalDateTime.now());
            saveEntity(sysUser);
        }
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", key = "#id")
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void remove(String id) {
        batchRemove(Collections.singletonList(id));
        // 删除关联角色
        sysUserRoleService.removeByUserId(id);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void batchRemove(List<String> ids) {
        ids.stream().map(this::fetchById)
                .filter(sysUser -> !sysUser.getStatus().equals(CommFlagType.Initial))
                .forEach(sysUser -> sysUser.setDelFlag(YnEnum.Y));
        repository.flush();
    }

    @Override
    public void delete(String id) {
        SysUser entity = fetchById(id);
        deleteAndFlush(entity);
        // 删除关联角色
        sysUserRoleService.removeByUserId(id);
    }

    @Override
    public long checkUsernameExists(String id, String username) {
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N));
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysUser.id.ne(id));
        }
        builder.and(qSysUser.username.eq(username));
        return repository.count(builder);
    }

    @Override
    public long checkEmailExists(String id, String email) {
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N));
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysUser.id.ne(id));
        }
        builder.and(qSysUser.email.eq(email));
        return repository.count(builder);
    }

    @Override
    public long checkMobileExists(String id, String mobile) {
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N));
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysUser.id.ne(id));
        }
        builder.and(qSysUser.mobile.like("%" + mobile.substring(6)));
        return repository.count(builder);
    }

    @Override
    public long checkUserCodeExists(String id, String userCode) {
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N));
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysUser.id.ne(id));
        }
        builder.and(qSysUser.userCode.eq(userCode));
        return repository.count(builder);
    }

    @CacheEvict(value = BASE_REDIS_CACHE_PREFIX + "user", allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void resetPwd(List<String> ids) {
        ids.forEach(id -> {
            SysUser entity = fetchById(id);
            entity.setPassword(passwordEncoder.encode(USER_P_INIT_VALUE));
            entity.setUpdateDate(LocalDateTime.now());
            entity.setPwdModifyDate(LocalDateTime.now());
            saveEntity(entity);
        });
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUser modifyPwd(String username, PwdQo pwdQo) {
        Assert.notNull(username, "用户未登录");
        SysUser entity = queryOne(qSysUser.username.eq(username).and(qSysUser.delFlag.eq(YnEnum.N)))
                .orElseThrow(() -> new RestApiException("用户名或密码错误"));//用户【" + username + "】不存在
        if (!passwordEncoder.matches(pwdQo.getOldPwd(), entity.getPassword())) {
            throw new RestApiException("原密码不正确");
        }
        entity.setPassword(passwordEncoder.encode(pwdQo.getPwd()));
        entity.setPwdModifyDate(LocalDateTime.now());
        if (entity.getPwdLimitDays() != null && entity.getPwdLimitDays() > 0) {
            entity.setPwdLimitDays(-1);
        }
        saveEntity(entity);
        return entity;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void recordLoginDate(String username) {
        SysUser sysUser = fetchOne(qSysUser.username.eq(username).and(qSysUser.delFlag.eq(YnEnum.N)));
        if (sysUser.getFirstLoginDate() == null) {
            sysUser.setFirstLoginDate(LocalDateTime.now());
        }
        repository.flush();
    }

    @Override
    public UserQo getByUserId(String userId) {
        Assert.notNull(userId, "用户标识不能为空");
        SysUser entity = repository.findById(userId).orElse(new SysUser());
        UserQo userQo = dtoMapper.map(entity, UserQo.class);
        if (!Strings.isNullOrEmpty(entity.getId())) {
            List<RoleQo> list = sysUserRoleService.findRolesByUserId(entity.getId());
            userQo.setRoles(list);
            boolean isAdminFlag = list.parallelStream()
                    .anyMatch(role -> role.getCode().equals(RoleEnum.ROLE_ADMIN.name()) ||
                            role.getRoute().contains(RoleEnum.ROLE_ADMIN.name()));
            userQo.setIsAdmin(isAdminFlag);
        }
        return userQo;
    }

    @Override
    public Map<String, Object> getByShareUserId(String userId) {
        Map<String, Object> map = Maps.newHashMap();
        Assert.notNull(userId, "用户标识不能为空");
        SysUser entity = repository.findById(userId).orElse(new SysUser());
        ExternalUserQo userQo = dtoMapper.map(entity, ExternalUserQo.class);
        buildUserInfo(map, userQo);
        return map;
    }

    @Override
    public void syncSubsystem(String id) {
        // todo
    }

    @Override
    public void syncPublic(String id) {
        // todo
    }

    @Override
    public UserQo getByToken(String token) {
        JwtTokenUtil.JwtUser user = jwtTokenUtil.getUserFromToken(token);
        SysUser sysUser = this.queryById(user.getId()).orElse(null);
        if (Objects.isNull(sysUser)) {
            return null;
        }
        UserQo qo = dtoMapper.map(sysUser, UserQo.class);
        qo.setIsAdmin(user.isAdmin());
        qo.setPassword(null);
        return qo;
    }

    @Override
    @Cacheable(value = USER_INFO_KEY, key = "#user.id", unless = "#result == null")
    public UserInfoQo gatherUserInfo(JwtTokenUtil.JwtUser user) {
        SysUser sysUser = this.queryById(user.getId()).orElse(null);
        if (Objects.isNull(sysUser)) {
            return null;
        }
        DepartQo depart = sysDepartService.findById(sysUser.getDepartId());
        UserInfoQo userInfoQo = new UserInfoQo();
        userInfoQo.setId(user.getId());
        userInfoQo.setIsAdmin(user.isAdmin());
        userInfoQo.setDepartId(sysUser.getDepartId());
        userInfoQo.setDepartRouteCode(depart.getRouteCode());
        userInfoQo.setDepartType(depart.getDepartKind());
        userInfoQo.setGatherAuth(genGatherAuth(user.getAuth()));
        userInfoQo.setRealName(sysUser.getRealName());
        if (DepartType.DOMAIN.name().equals(depart.getDepartKind())) {
            userInfoQo.setDomainDepartId(sysUser.getDepartId());
            userInfoQo.setRegionIdPrefix(genRegionIdPrefix(depart.getRegionId(), depart.getLevel()));
        } else {
            DepartQo parentDepart = sysDepartService.findById(depart.getParentId());
            userInfoQo.setDomainDepartId(parentDepart.getId());
            // 页面非必传，但是此处有截取字符串
//            userInfoQo.setRegionIdPrefix(genRegionIdPrefix(parentDepart.getRegionId(), depart.getLevel()));
        }

        return userInfoQo;
    }

    private String genRegionIdPrefix(String regionId, Integer level) {
        // 根据区域编码查询
        if (Objects.isNull(level) || level == 0) {
            return "NULL";
        }

        if (level <= 4) {
            return regionId.substring(0, 2 * (level - 1));
        }

        if (level == 5) {
            return regionId.substring(0, 9);
        }

        if (level == 6) {
            return regionId;
        }
        return "NULL";
    }

    @Override
    public List<String> queryUserIdsByDeptId(String deptId) {
        return queryAll(qSysUser.departId.eq(deptId).and(qSysUser.delFlag.eq(YnEnum.N)).and(qSysUser.status.eq(CommFlagType.Enabled))
                .and(qSysUser.id.in(JPAExpressions.select(qSysUserRole.userId).from(qSysUserRole)
                        .where(qSysUserRole.roleId.in(JPAExpressions.select(qSysRole.id).from(qSysRole).where(qSysRole.code.eq(RoleEnum.ROLE_QU_ADMIN.name())))))))
                .stream().map(SysUser::getId).collect(Collectors.toList());
    }

    private String genGatherAuth(String auth) {
        // 1. 获取用户权限
        Set<String> authorizes = StringUtils.commaDelimitedListToSet(auth);
        if (authorizes.contains(RoleEnum.ROLE_QU_ADMIN.name())) {
            return RoleEnum.ROLE_QU_ADMIN.name();
        } else {
            long num = authorizes.stream().filter(s -> s.startsWith(RoleEnum.XYGXJH_CJY.name())).count();
            if (num > 0) {
                return RoleEnum.XYGXJH_CJY.name();
            }
        }

        return "";
    }

    /**
     * 构建用户信息map
     *
     * @param map    用户信息map
     * @param userQo 用户信息dto
     */
    private void buildUserInfo(Map<String, Object> map, ExternalUserQo userQo) {
        map.put("username", userQo.getUsername());
        map.put("realName", userQo.getRealName());
        map.put("createDate", userQo.getCreateDate().format(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)));
        map.put("mobile", userQo.getMobile());
        map.put("pwdModifyDate", userQo.getPwdModifyDate().format(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)));
        map.put("infoSource", userQo.getInfoSource());
        map.put("idType", userQo.getIdType());
        map.put("idCode", userQo.getIdCode());
        map.put("remark", userQo.getRemark());
        map.put("email", userQo.getEmail());
        DepartQo depart = sysDepartService.findById(userQo.getDepartId());
        map.put("departName", depart.getDepartName());
    }

    @Override
    public List<UserQo> getByUserId(List<String> userIds) {
        Assert.notEmpty(userIds, "用户标识列表不能为空");
        List<SysUser> list = queryAll(qSysUser.id.in(userIds));
        return list.stream().map(sysUser -> dtoMapper.map(sysUser, UserQo.class)).collect(Collectors.toList());
    }

    @Override
    public List<UserQo> getByUserAll() {
        List<SysUser> list = queryAll(qSysUser.delFlag.in(YnEnum.N));
        return list.stream().map(sysUser -> dtoMapper.map(sysUser, UserQo.class)).collect(Collectors.toList());
    }

    @Override
    public UserQo getByUsername(String username) {
        Optional<SysUser> optional = repository.findOne(qSysUser.username.eq(username).and(qSysUser.delFlag.eq(YnEnum.N)));
        return optional.map(sysUser -> {
            UserQo userQo = dtoMapper.map(sysUser, UserQo.class);
            List<RoleQo> list = sysUserRoleService.findRolesByUserId(Objects.requireNonNull(sysUser).getId());
            userQo.setRoles(list);
            return userQo;
        }).orElse(null);
    }

    @Override
    public Page<UserQo> searchLeader(Pageable pageable, MultiValueMap<String, String> parameters) {
        String departId = parameters.getFirst("departId");
        String name = parameters.getFirst("userName");
        if (pageable.getSort().isUnsorted()) {
            pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(),
                    QSort.by(qSysUser.sortNum.asc(), qSysUser.createDate.desc()));
        }
        List<String> departIds = Lists.newArrayList(departId);
        DepartQo departQo = sysDepartService.findById(departId);
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N).and(qSysUser.status.eq(CommFlagType.Enabled)));
        getDepartIds(departIds, departQo);


        if (!StringUtils.isEmpty(name)) {
            builder.and(qSysUser.username.contains(name));
        }
        builder.and(qSysUser.departId.in(departIds));
        return queryByPage(builder, pageable).map(entity -> {
            UserQo userQo = dtoMapper.map(entity, UserQo.class);
            DepartQo departInfo = sysDepartService.findById(userQo.getDepartId());
            userQo.setDepartName(departInfo.getDepartName());
            return userQo;
        });
    }

    @Override
    public Page<String> getByDepartId(String departId, Pageable pageable) {
        Page<SysUser> page = repository.findAll(qSysUser.departId.eq(departId), pageable);
        return page.map(SysUser::getId);
    }

    private void getDepartIds(List<String> departIds, DepartQo departQo) {
        if (departQo.getDepartKind().equals(DepartType.DEPART.name())) {
            DepartQo depart = sysDepartService.findById(departQo.getParentId());
            getDepartIds(departIds, depart);
        } else {
            departIds.addAll(sysDepartService.queryChildrenIdsById(departQo.getId()));
        }
    }

    @Override
    @Transactional
    public void setPost(Map<String, Object> params) {
        Preconditions.checkArgument(Objects.nonNull(params.get("userId")), "用户信息不能为空");
        Preconditions.checkArgument(Objects.nonNull(params.get("departIds")), "岗位信息不能为空");
//        先根据删除用户所有岗位信息
        userPostRepository.deleteAll(
                userPostRepository.findAll(userPostEntity.userId.eq(params.get("userId").toString())));

//        保存用户岗位信息
        List<NcloudSysUserPostEntity> userPostEntityList = Lists.newLinkedList();

        List<String> postIds = (List<String>) params.get("departIds");
        postIds.forEach(postId -> {
            NcloudSysUserPostEntity userPost = new NcloudSysUserPostEntity();
            userPost.setUserId(params.get("userId").toString());
            userPost.setDepartId(postId);
            userPost.setCreateBy(AuthenticationFacade.getUserId());
            userPost.setCreateDate(LocalDateTime.now());
            userPostEntityList.add(userPost);
        });
        userPostRepository.saveAll(userPostEntityList);
    }

    @Override
    public List<DepartQo> getPostUserInfo() {
//        获取所有岗位信息
        List<DepartQo> positionDepartTree = sysDepartService.findPositionDepartTree();
//        获取岗位下的所有用户信息
        postUserInfoTree(positionDepartTree);
        return positionDepartTree;
    }


    @Override
    public UserQo getUserByUserName(UserQo userQo) {
        return StreamSupport.stream(this.repository.findAll(qSysUser.username.eq(userQo.getUsername()).and(qSysUser.delFlag.eq(YnEnum.N))).spliterator(), false)
                .findAny().map(entity -> dtoMapper.map(entity, UserQo.class)).orElse(null);
    }


    private void postUserInfoTree(List<DepartQo> positionDepartTree) {
        positionDepartTree.forEach(position -> {
            if (position.getDepartKind().equals(DepartType.POSITION.name())) {
                position.setUserList(repository.findAll(jpqlQuery -> jpqlQuery.from(qSysUser).innerJoin(userPostEntity)
                        .on(qSysUser.id.eq(userPostEntity.userId))
                        .where(qSysUser.delFlag.eq(YnEnum.N).and(userPostEntity.departId.eq(position.getId()))), qSysUser)
                        .stream().map(sysUser -> dtoMapper.map(sysUser.get(qSysUser), UserDetail.class))
                        .collect(Collectors.toList()));
            }
            if (!Objects.isNull(position.getChildren())) {
                postUserInfoTree(position.getChildren());
            }
        });
    }

    @Override
    public long checkIdCodeExists(String id, String idCode) {
        BooleanBuilder builder = new BooleanBuilder(qSysUser.delFlag.eq(YnEnum.N));
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysUser.id.ne(id));
        }
        builder.and(qSysUser.idCode.eq(idCode));
        return repository.count(builder);
    }

    /**
     * 根据角色类型查询用户
     *
     * @param roleCode 角色编码
     * @return 用户信息
     */
    @Override
    public List<UserQo> queryUserByRole(String roleCode) {
        return queryAll(qSysUser.delFlag.eq(YnEnum.N).and(qSysUser.status.eq(CommFlagType.Enabled))
                .and(qSysUser.id.in(JPAExpressions.select(qSysUserRole.userId).from(qSysUserRole)
                        .where(qSysUserRole.roleId.in(JPAExpressions.select(qSysRole.id).from(qSysRole).where(qSysRole.code.eq(roleCode)))))))
                .stream().map(sysUser -> dtoMapper.map(sysUser, UserQo.class)).collect(Collectors.toList());
    }

    /**
     * 修改用户信息 清除缓存
     */
    private void clearRedis() {
        if (redisTemplate.hasKey(USER_INFO_KEY)) {
            redisTemplate.delete(USER_INFO_KEY);
        }
    }
}
